﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CAN_Monitor
{
    class PacketFilter
    {
        #region ctor

        /// <summary>
        /// Standardconstructor
        /// </summary>
        /// <param name="filterString">A string which defines the filter rules</param>
        public PacketFilter(String filterString)
        {
            this.mFilterString = filterString;
            if (!this.BuildRules())
            {
                throw new Exception("Error when parsing the filter string!");
            }
        }

        #endregion

        #region Members

        private String mFilterString = null;
        private List<FilterRule> mActiveRules = new List<FilterRule>();
        private bool mDefaultRule = true;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the filterstring which defines the filterrules (invokes a rule build)
        /// </summary>
        public String FilterString
        {
            get
            {
                return mFilterString;
            }
            set
            {
                mFilterString = value;
                this.BuildRules();
            }
        }

        /// <summary>
        /// Gets or sets the default rule if no rules are defined
        /// </summary>
        public bool DefaultRule
        {
            get
            {
                return this.mDefaultRule;
            }
            set
            {
                mDefaultRule = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Parses the filterstring and builds rules from it
        /// </summary>
        /// <returns>True if everything was ok otherwise false</returns>
        private bool BuildRules()
        {
            bool success = true;

            string[] splits = this.mFilterString.Split(';');    //Split whole string into seperate rules (prority 0)
            string[] ruleValues = null;

            //Add all rules from the string
            foreach (string ruleStr in splits)
            {
                FilterRule rule = null;

                if((rule = this.BuildRule(ruleStr)) != null)
                {
                    //Add new rule
                    this.mActiveRules.Add(rule);
                }//if
            }//foreach

            return success;
        }

        private FilterRule BuildRule(string ruleStr)
        {
            FilterRule rule = null;
            string[] ruleValues = null;

            

            //Build rules

            //Priority 0
            if((ruleValues = ruleStr.Split(new string[] { "&&" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
            {
                rule = new FilterRule(this.BuildRule(ruleValues[0]), FilterRuleOperator.AND, this.BuildRule(ruleValues[1]));
            }
            else if ((ruleValues = ruleStr.Split(new string[] { "||" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
            {
                rule = new FilterRule(this.BuildRule(ruleValues[0]), FilterRuleOperator.OR, this.BuildRule(ruleValues[1]));
            }
            else
            {
                //Priority 1
                if ((ruleValues = ruleStr.Split(new string[] { "==" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
                {
                    rule = (new FilterRule(ruleValues[0], FilterRuleOperator.EQUALS, new FilterValue(ruleValues[1])));
                }
                else if ((ruleValues = ruleStr.Split(new string[] { "!=" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
                {
                    rule = (new FilterRule(ruleValues[0], FilterRuleOperator.NOT_EQUALS, new FilterValue(ruleValues[1])));
                }
                else if ((ruleValues = ruleStr.Split(new string[] { ">=" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
                {
                    rule = (new FilterRule(ruleValues[0], FilterRuleOperator.GREATER_EQUAL, new FilterValue(ruleValues[1])));
                }
                else if ((ruleValues = ruleStr.Split(new string[] { "<=" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
                {
                    rule = (new FilterRule(ruleValues[0], FilterRuleOperator.LESS_EQUAL, new FilterValue(ruleValues[1])));
                }
                else if ((ruleValues = ruleStr.Split(new string[] { "<" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
                {
                    rule = (new FilterRule(ruleValues[0], FilterRuleOperator.LESS, new FilterValue(ruleValues[1])));
                }
                else if ((ruleValues = ruleStr.Split(new string[] { ">" }, StringSplitOptions.RemoveEmptyEntries)).Length == 2)
                {
                    rule = (new FilterRule(ruleValues[0], FilterRuleOperator.GREATER, new FilterValue(ruleValues[1])));
                }
            }

            return rule;
        }

        //----------------------------------------------------------------------------------------

        /// <summary>
        /// Checks if a packet matches the active filter rules
        /// </summary>
        /// <param name="packet">The packet that sould be checked</param>
        /// <returns>True if the packet matches the filter otherwise false</returns>
        public bool MatchesFilter(CANLib.CANPacket packet)
        {
            bool matches = this.mDefaultRule;

            foreach (FilterRule rule in mActiveRules)
            {
                //Check if current rule matches with packet
                if (rule.Check(packet) != mDefaultRule)
                {
                    matches = !mDefaultRule;

                    //Stop when one rule doesn't match
                    break;
                }
            }

            return matches;
        }

        //----------------------------------------------------------------------------------------

        #endregion
    }
}
